#include "os.h"

#include "task.h"
#include "ARMCM3.h"



/* 中断控制及状态寄存器ICSR*/
#define NVIC_INT_CTRL       0xE000ED04
/* 悬起PendSV中断*/
#define NVIC_PENDSV_SET     0x10000000
/* 系统异常优先级寄存器*/
/* PendSV的优先级*/
#define NVIC_SYSPRI14       0xE000ED22
#define NVIC_PENDSV_PRI     0x000000FF

#define MEM32(addr)         *(volatile unsigned long *)(addr)
#define MEM8(addr)          *(volatile unsigned char *)(addr)
    

void os_setPendSV_PRI(void)
{
    /* 设置pendsv的优先级*/
    MEM8(NVIC_SYSPRI14) = NVIC_PENDSV_PRI;
}




/*****************************************************************************
 * @function        os_taskSwitch
 * @brief 			上下文切换.即触发pendsv中断，进行任务调度
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void os_taskSwitch(void)
{
    /* 挂起pendsv中断--进行上下文切换*/
    MEM32(NVIC_INT_CTRL) |= NVIC_PENDSV_SET;
}



/*****************************************************************************
 * @function        vTaskEnterCritical
 * @brief 			进入临界区.
 *
 * 此函数用于线程中 进入临界区保护，返回中断使能配置用于退出临界区操作.
 * 
 * @return 			返回 进入临界区前的中断使能配置primask .	
 *
 * ***************************************************************************/
uint32_t vTaskEnterCritical(void)
{
    /* 保存中断使能配置*/
    uint32_t primask = __get_PRIMASK();
    
    /* 关中断*/
    __disable_irq();
    
    return primask;
//    return 0;
}


/*****************************************************************************
 * @function        vTaskExitCritical
 * @brief 			退出临界区.
 *
 * 此函数用于线程中退出临界区保护操作.需要填入 进入临界区前的中断使能配置
 *  
 * @param[in] 		primask 进入临界区前的中断使能配置.
 * 
 * @return 			None.
 *
 * ***************************************************************************/
void vTaskExitCritical(uint32_t primask)
{
    /* 恢复中断使能配置*/
    __set_PRIMASK(primask);
}



/*****************************************************************************
 * @function        PendSV_Handler
* @brief 			PendSV中断服务程序.
 *
 * 此函数用于进行上下文切换。
 * 
 * @return 			None.
 *
 * ***************************************************************************/
__asm void PendSV_Handler (void)
{
    // 类似extern 声明外部变量
    IMPORT curTask
    IMPORT nextTask
    
    /* 判断PSP线程堆栈是否为0，以分辨首个任务*/
    MRS R0, PSP
    CBZ R0, PendSVHandle_nosave
    /* 保存当前任务现场---当前任务的任务堆栈指针在 PSP中存放过，直接使用*/
//    LDR R0, =curTask
//    LDR R0, [R0]
//    LDR R0, [R0]
    STMDB R0!, {R4-R11}
    
    /* 更新当前任务堆栈指针*/
    LDR R1, =curTask
    LDR R1, [R1]
    STR R0, [R1]
    
    
PendSVHandle_nosave
    /* 更新当前任务指针*/
    LDR R0, =curTask
    LDR R1, =nextTask
    LDR R2, [R1]
    STR R2, [R0]
    
    /* 恢复现场*/
    LDR R0, [R2]
    LDMIA R0!, {R4-R11}
    
    /* 更新PSP进程堆栈指针--？？？为什么要有这一步
     * 相当于把任务堆栈指针 存放到进程堆栈中，以便进行任务切换时，方便进行堆栈保护。
     */
    MSR PSP, R0
    /* 从进程堆栈中做 出栈操作，返回后使用PSP*/
    ORR LR, LR, #0x04
    
    // 无条件跳转
    BX LR
}



/*****************************************************************************
 * @function        SystickInit
 * @brief 			滴答时钟初始化.
 *
 * 此函数用于配置滴答时钟，并开启滴答时钟中断, 提供上下文切换功能
 * 
 * @return 			None	
 *
 * ***************************************************************************/
void SystickInit(uint32_t ms)
{
    SysTick->LOAD = ms * SystemCoreClock / 1000 - 1;
    NVIC_SetPriority(SysTick_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
    SysTick->VAL = 0;
    SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
                    SysTick_CTRL_TICKINT_Msk |
                    SysTick_CTRL_ENABLE_Msk;
}


/*****************************************************************************
 * @function        SysTick_Handler
 * @brief 			滴答时钟中断处理函数.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void SysTick_Handler(void)
{
    vTaskSystemTickHandle();
}


